package com.windward.vii.service;

import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import com.alibaba.fastjson.JSON;
import com.windward.vii.common.Constants;
import com.windward.vii.common.JsonResult;
import com.windward.vii.common.ResultCode;
import com.windward.vii.config.PlatformConfig;
import com.windward.vii.config.PushConfig;
import com.windward.vii.config.RandomPoolConfig;
import com.windward.vii.dao.entity.*;
import com.windward.vii.dao.mapper.*;
import com.windward.vii.framework.RedisManager;
import com.windward.vii.service.apns.PushService;
import com.windward.vii.service.jpush.api.JPushClient;
import com.windward.vii.service.rongcloud.RongCloudProxy;
import com.windward.vii.service.rongcloud.messages.extend.CallMessage;
import com.windward.vii.service.rongcloud.messages.extend.HandupMessage;
import com.windward.vii.service.rongcloud.messages.extend.RandomCallMessage;
import com.windward.vii.service.rongcloud.models.CodeSuccessResult;
import com.windward.vii.utils.BigDecimalUtil;
import com.windward.vii.utils.DateUtils;
import com.windward.vii.utils.StringUtils;
import com.windward.vii.utils.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;


/**
 * Created by tiansj on 2017/8/27.
 */
@Component
public class ChatService {

    private static Logger logger = LoggerFactory.getLogger(ChatService.class);

    @Autowired
    PlatformTransactionManager transactionManager;
    @Resource
    ChatMapper chatMapper;
    @Resource
    UserService userService;
    @Resource
    AssetUserAccountMapper assetUserAccountMapper;
    @Resource
    AssetUserConsumeMapper assetUserConsumeMapper;
    @Resource
    AssetUserIncomeMapper assetUserIncomeMapper;
    @Resource
    AssetPlatformProfitMapper assetPlatformProfitMapper;
    @Resource
    AssetPlatformProfitRateMapper assetPlatformProfitRateMapper;
    @Resource
    PlatformConfig platformConfig;
    @Resource
    RandomPoolUserMapper randomPoolUserMapper;
    @Resource
    OrganizationMapper organizationMapper;
    @Resource
    RedisManager redisManager;
    @Resource
    RongCloudProxy rongCloudProxy;
    @Resource
    UserAttentionMapper userAttentionMapper;
    @Resource
    RandomPoolConfig randomPoolConfig;
    @Resource
    PushConfig pushConfig;
    @Resource
    AppRecordService appRecordService;
    @Resource
    AssetPlatformAccountMapper assetPlatformAccountMapper;
    @Resource
    BlacklistService blacklistService;
    @Resource
    DeviceMapper deviceMapper;

    /**
     * 随机聊天接口：客户端5秒调用一次接口
     * @param uid
     * @param sex
     * @return
     */
    public JsonResult randomChatV2(Long uid, Integer sex) {
        logger.info("randomChat: begin {}--------------------------------------", System.currentTimeMillis());
        logger.info("randomChat: uid = {}, sex = {}", uid, sex);
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            // 每秒一次接口调用，防止被连接的人已经接通，但还没收到im或声网通知，又开始调用
            Chat dbChat = chatMapper.selectActivityRandomChatByUid(uid);
            // 3秒内创建的chat，有效，直接返回；3秒时长，是根据接通后，3秒后才能挂断。
            if(dbChat != null) {
                logger.info("randomChat: Chat已匹配，chatId = {}，对方uid={}", dbChat.getId(), dbChat.getUid());
                Date rangeDate = DateUtils.addSecond(new Date(), -10);
                if(DateUtils.compareTime(dbChat.getBeginTime(), rangeDate, Calendar.SECOND) > 0) {
                    logger.info("randomChat: Chat已匹配，而且时间小于10秒，直接返回");
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("chatId", dbChat.getId());
                    map.put("anchorId", dbChat.getUid());
                    // 查询关注关系，是否已互相关注
                    UserAttentionKey userAttentionKey1 = new UserAttentionKey();
                    userAttentionKey1.setUid(uid);
                    userAttentionKey1.setAttentionUid(dbChat.getUid());
                    UserAttention dbUserAttention = userAttentionMapper.selectByPrimaryKey(userAttentionKey1);
                    map.put("isFriend", dbUserAttention != null && dbUserAttention.getStatus() == 2);
                    logger.info("randomChat: isFriend = {}", map.get("isFriend"));
                    JsonResult jsonResult = JsonResult.success();
                    jsonResult.setBody(map);
                    isSuccess = true;
                    return jsonResult;
                }
            }

            // 寻找随机池中正在匹配的人，没有过期的用户，理论上只有一个，同时在池中。
            List<RandomPoolUser> randomPoolUserList = null;
            if(sex != null && sex > 0 && sex < 3) {
                randomPoolUserList = randomPoolUserMapper.getOnlineListBySexForUpdate(uid, sex, new Date());
            } else {
                randomPoolUserList = randomPoolUserMapper.getOnlineListForUpdate(uid, new Date());
            }

            // 查看自己是否在池子中，客户端每秒调用一次
            RandomPoolUser self = randomPoolUserMapper.selectRandomPoolUserByUid(uid, new Date());

            // 没有匹配上，把自己放入随机池，有效时间为4秒
            if(randomPoolUserList == null || randomPoolUserList.size() == 0) {
                logger.info("randomChat: 用户池为空");
                if(self == null) { // 没在池中，才放入。等待别人连接
                    // 放入随机池中，create_time和expire_time为4秒。
                    RandomPoolUser randomPoolUser = new RandomPoolUser();
                    AssetUserAccount anchorUserAccount = assetUserAccountMapper.selectByUid(uid);
                    randomPoolUser.setUid(uid);
                    randomPoolUser.setSex(anchorUserAccount.getSex());
                    randomPoolUser.setStatus(Constants.RandomPoolUser.STATUS_INIT);
                    Date date = new Date();
                    randomPoolUser.setCreateTime(date);
                    randomPoolUser.setUpdateTime(date);
                    randomPoolUser.setExpireTime(DateUtils.addSecond(date, randomPoolConfig.getExpireTime())); // 有效时间为4秒
                    if(randomPoolUserMapper.insertSelective(randomPoolUser) < 0) {
                        return JsonResult.error("创建随机匹配记录失败，请重试");
                    }
                    // 设置自己为BUSY状态
                    if(!userService.updateOnlineStatusById(uid, Constants.User.ONLINE_STATUS_BUSY)) {
                        return JsonResult.error("更新自己为忙碌失败，请重试");
                    }
                    logger.info("randomChat: 当前用户不在用户池，创建新的randomPoolUser id={}", randomPoolUser.getId());
                } else {
                    logger.info("randomChat: 当前用户已在用户池，而且没过期，直接返回randomPoolUser id={}", self.getId());
                }
                isSuccess = true;
                return JsonResult.error("当前没有在线用户，您已进入随机池，等待其他用户匹配");
            } else {    // 找到匹配的用户，自己不用进入随机池，修改该用户在池中失效，形成Chat关系即可
                // 混乱list
                Collections.shuffle(randomPoolUserList);
                // 随机选择一位
                Long anchorUid = randomPoolUserList.get(0).getUid();
                Long anchorRandomId = randomPoolUserList.get(0).getId();
                logger.info("randomChat: 用户池不为空，选择用户id={}", anchorUid);
                // 创建聊天关系
                Chat chat = new Chat();
                chat.setUid(uid);
                chat.setAnchorUid(anchorUid);
                chat.setBeginTime(new Date());
                chat.setUpdateTime(new Date());
                chat.setMinutes(0);
                chat.setStatus(Constants.Chat.STATUS_ACCEPT);
                chat.setType(Constants.Chat.TYPE_RANDOM);
                int row = chatMapper.insertSelective(chat);
                if(row < 1) {
                    return JsonResult.error("创建聊天关系失败，请重试");
                }
                logger.info("randomChat: 创建聊天关系成功，chatId={}", chat.getId());

                // 设置双方为忙碌状态
                if (!userService.updateOnlineStatusById(chat.getUid(), Constants.User.ONLINE_STATUS_BUSY)) {
                    return JsonResult.error("更新自己为在线状态失败，请重试");
                }
                if (!userService.updateOnlineStatusById(chat.getAnchorUid(), Constants.User.ONLINE_STATUS_BUSY)) {
                    return JsonResult.error("更新对方为在线状态失败，请重试");
                }

                if(self != null) {
                    // 更新自己状态
                    RandomPoolUser updatePoolUser = new RandomPoolUser();
                    updatePoolUser.setId(self.getId());
                    updatePoolUser.setTargetId(anchorRandomId);
                    updatePoolUser.setAnchorUid(anchorUid);
                    updatePoolUser.setChatId(chat.getId());
                    updatePoolUser.setStatus(Constants.RandomPoolUser.STATUS_CONNECTED);
                    updatePoolUser.setUpdateTime(new Date());
                    // 非阻塞项，不判断成功状态
                    randomPoolUserMapper.updateByPrimaryKeySelective(updatePoolUser);
                    logger.info("randomChat: self != null，update status = 3");
                }

                // 更新池中数据：已连接，已失效
                RandomPoolUser updateTargetPoolUser = new RandomPoolUser();
                updateTargetPoolUser.setId(anchorRandomId);
                if(self != null) {
                    updateTargetPoolUser.setTargetId(self.getId());
                }
                updateTargetPoolUser.setAnchorUid(uid);
                updateTargetPoolUser.setChatId(chat.getId());
                updateTargetPoolUser.setStatus(Constants.RandomPoolUser.STATUS_CONNECTED);
                updateTargetPoolUser.setUpdateTime(new Date());
                if(randomPoolUserMapper.updateByPrimaryKeySelective(updateTargetPoolUser) < 1) {
                    return JsonResult.error("更新RandomPoolUser状态失败，请重试");
                }
                logger.info("randomChat: updateTargetPoolUser，update status = 3");
                isSuccess = true;
                HashMap<String, Object> map = new HashMap<>();
                map.put("chatId", chat.getId());
                map.put("anchorId", anchorUid);
                // 查询关注关系，是否已互相关注
                UserAttentionKey userAttentionKey1 = new UserAttentionKey();
                userAttentionKey1.setUid(uid);
                userAttentionKey1.setAttentionUid(anchorUid);
                UserAttention dbUserAttention = userAttentionMapper.selectByPrimaryKey(userAttentionKey1);
                map.put("isFriend", dbUserAttention != null && dbUserAttention.getStatus() == 2);
                logger.info("randomChat: isFriend = {}", map.get("isFriend"));
                // 异步发送IM通知，已变为客户端发送im
                // asyncSendRandomConnectSuccessMsg(String.valueOf(uid), String.valueOf(anchorUid), String.valueOf(chat.getId()));
                JsonResult jsonResult = JsonResult.success();
                jsonResult.setBody(map);
                return jsonResult;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
            logger.info("randomChat: transaction isSuccess = {}", isSuccess);
            logger.info("randomChat: end {}--------------------------------------", System.currentTimeMillis());
        }
    }

    /**
     * 任何一方挂断随机聊天
     * 1. 只更新Chat状态，和随机池已无关
     * @param uid
     * @return
     */
    public JsonResult randomAbortV2(Long uid) {
        logger.info("randomChatAbort: begin {}--------------------------------------", System.currentTimeMillis());
        logger.info("randomChatAbort: uid = {}", uid);
        if(uid != null && uid > 0) {
            logger.info("randomChatAbort: 更新自己uid={}为在线状态", uid);
            userService.updateOnlineStatusById(uid, Constants.User.ONLINE_STATUS_YES);
        }

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            // 可能两个人同时都挂断，所以做一层防护
            Chat dbChat = chatMapper.selectActivityRandomChatByUid(uid);
            if(dbChat != null) {
                Chat updateChat = new Chat();
                updateChat.setId(dbChat.getId());

                Date date = new Date();
                updateChat.setUpdateTime(date);
                updateChat.setEndTime(date);
                // 计算差多少分钟
                int minutes = (int) ((date.getTime() - dbChat.getBeginTime().getTime()) / (1000 * 60));
                updateChat.setMinutes(minutes + 1);
                updateChat.setStatus(Constants.Chat.STATUS_USER_ABORT);
                chatMapper.updateByPrimaryKeySelective(updateChat);
                logger.info("randomChatAbort: 终止Chat, id={}, uid={}, anchorUid={}", dbChat.getId(), dbChat.getUid(), dbChat.getAnchorUid());
                // userService.updateOnlineStatusById(dbChat.getAnchorUid(), Constants.User.ONLINE_STATUS_YES);
                // logger.info("randomChatAbort: 更新对方uid={}为在线状态", dbChat.getAnchorUid());
            } else {
                logger.info("randomChatAbort: dbChat为空，可能已被对方终止");
            }
            isSuccess = true;
            return JsonResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
            logger.info("randomChatAbort: transaction isSuccess = {}", isSuccess);
            logger.info("randomChatAbort: end {}--------------------------------------", System.currentTimeMillis());
        }
    }


    // 异步线程池发送im消息，防止阻塞数据库事务
    private void asyncSendRandomConnectSuccessMsg(final String fromUserId, final String toUserId, final String chatId) {
        ThreadPoolManager.getInstance().addExecuteTask(new Runnable() {
            @Override
            public void run() {
                try {
                    String[] messagePublishSystemToUserId = {toUserId};
                    RandomCallMessage randomCallMessage = new RandomCallMessage(chatId, fromUserId);
                    CodeSuccessResult messagePublishSystemResult = rongCloudProxy.getInstance().message.PublishSystem(fromUserId,
                            messagePublishSystemToUserId, randomCallMessage,
                            "", "",
                            0, 0);
                    if(messagePublishSystemResult.getCode() != 200) {
                        logger.error("随机连接，发送IM消息通知失败，CodeSuccessResult={}, RandomCallMessage={}",
                                JSON.toJSONString(messagePublishSystemResult),
                                JSON.toJSONString(randomCallMessage));
                    } else {
                        logger.info("随机连接，发送IM消息通知成功，RandomCallMessage={}",
                                JSON.toJSONString(randomCallMessage));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }


    public JsonResult consume(Long chatId, Long uid, Long anchorUid, String userAgent) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            logger.info("consume: begin {}--------------------------------------", System.currentTimeMillis());
            logger.info("consume: chatId={}, uid={}, anchorUid={}", chatId, uid, anchorUid);
            // 检查聊天关系和状态
            Chat chat = chatMapper.selectByIdForUpdate(chatId);
            if(chat == null || chat.getStatus() != Constants.Chat.STATUS_ACCEPT) {
                logger.error("consume: 聊天可能已被对方终止，chatId={}, status={}", chatId);
                if(chat != null) {
                    logger.error("consume: 当前聊天状态，status={}", chat.getStatus());
                }
                return new JsonResult(ResultCode.ERROR_CODE_701,"聊天已被终止");
            }
            if(!chat.getAnchorUid().equals(anchorUid) || !chat.getUid().equals(uid)) {
                logger.error("consume: chat.getUid()={}, chat.getAnchorUid()={}, 聊天关系无法对应", chat.getUid(), chat.getAnchorUid());
                return new JsonResult(ResultCode.ERROR_CODE_701,"聊天关系无法对应");
            }

            // 查询主播每分钟额度
            AssetUserAccount anchorUserAccount = assetUserAccountMapper.selectByUid(anchorUid);
            int price = anchorUserAccount.getPrice();

            boolean isInReviewVersion = appRecordService.checkIsInReview(userAgent);
            if(isInReviewVersion) {
                price = 0;
            }

            // 检查用户余额
            AssetUserAccount assetUserAccount = assetUserAccountMapper.selectByUidForUpdate(uid);
//            if(assetUserAccount.getAvailableRechargeCoin() + assetUserAccount.getAvailableIncomeCoin() < price) {
            if(assetUserAccount.getAvailableReceiveCoin() + assetUserAccount.getAvailableRechargeCoin() < price) {
                logger.info("consume: 播主每分钟价格={}", price);
                logger.error("consume: 用户可用V钻不足，getAvailableRechargeCoin={}, getAvailableReceiveCoin={}",
                        assetUserAccount.getAvailableRechargeCoin(),
                        assetUserAccount.getAvailableReceiveCoin());
                return new JsonResult(ResultCode.ERROR_CODE_702,"可用V钻不足，请充值");
            }

            // 主播/平台分成比例
            BigDecimal rate = null;
            Organization organization = null;
            if(anchorUserAccount.getOrganizationId() != null && anchorUserAccount.getOrganizationId() > 0) {
                // 查询机构分成比例
                organization = organizationMapper.selectByPrimaryKey(anchorUserAccount.getOrganizationId());
                if(organization != null) {
                    rate = organization.getProfitRate();
                }
            }
            if(rate == null) {
                // 查询平台分成比例
                AssetPlatformProfitRate assetPlatformProfitRate = assetPlatformProfitRateMapper.selectByType(Constants.AssetUserConsume.TYPE_CHAT);
                if(assetPlatformProfitRate != null) {
                    rate = assetPlatformProfitRate.getRate();
                }
            }
            if(rate == null) {
                // 系统默认设置
                rate = platformConfig.getChatProfitRate();
            }

            // 主播分成比例
            int anchorIncome = BigDecimalUtil.mul(new BigDecimal(price), rate).intValue();
            // 平台分成比例
            int platformIncome = price - anchorIncome;

            // 扣除用户的钱
            int row = 0;
            int availableReceiveCoin = assetUserAccount.getAvailableReceiveCoin();
            if(price > 0) {
                if (availableReceiveCoin >= price) {
                    row = assetUserAccountMapper.consumeReceiveIcon(uid, price);
                } else if (availableReceiveCoin > 0) {
                    row = assetUserAccountMapper.consumeReceiveIcon(uid, availableReceiveCoin);
                    if (row > 0) {
                        row = assetUserAccountMapper.consumeRechargeIcon(uid, price - availableReceiveCoin);
                    }
                } else {
                    row = assetUserAccountMapper.consumeRechargeIcon(uid, price);
                }
                if (row < 1) {
                    logger.error("consume: ERROR_CODE_703, 扣除用户V钻失败，请重试");
                    return new JsonResult(ResultCode.ERROR_CODE_703, "扣除用户V钻失败，请重试");
                }
            }

//            int row = 0;
//            if(price > 0) {
//                row = assetUserAccountMapper.consumeRechargeIcon(uid, price);
//                if(row < 1) {
//                    logger.error("consume: ERROR_CODE_703, 扣除用户V钻失败，请重试");
//                    return new JsonResult(ResultCode.ERROR_CODE_703,"扣除用户V钻失败，请重试");
//                }
//            }

            // 添加消费记录
            AssetUserConsume assetUserConsume = new AssetUserConsume();
            assetUserConsume.setUid(uid);
            if(assetUserAccount.getOrganizationId() != null) {
                assetUserConsume.setOrgId(assetUserAccount.getOrganizationId());
            }
            assetUserConsume.setAnchorUid(anchorUid);
            assetUserConsume.setCoin(price);
            assetUserConsume.setChatId(chatId);
            assetUserConsume.setType(Constants.AssetUserConsume.TYPE_CHAT);
            assetUserConsume.setCreateTime(new Date());
            row = assetUserConsumeMapper.insertSelective(assetUserConsume);
            if(row < 1) {
                logger.error("consume: ERROR_CODE_704, 生成用户消费记录失败，请重试");
                return new JsonResult(ResultCode.ERROR_CODE_704,"生成用户消费记录失败，请重试");
            }

            // 减少消费者对应机构的钱
//            if(assetUserAccount.getOrganizationId() != null && assetUserAccount.getOrganizationId() > 0) {
//                // 扣除金额
//                if(availableRechargeCoin < price) {
//                    int orgComsumePrice = price - availableRechargeCoin;
//                    row = organizationMapper.consume(assetUserAccount.getOrganizationId(), orgComsumePrice);
//                    if(row < 1) {
//                        logger.error("consume: ERROR_CODE_705, 扣除用户机构消费失败，请重试");
//                        return new JsonResult(ResultCode.ERROR_CODE_705,"扣除用户机构消费失败，请重试");
//                    }
//                }
//            }

            // 增加主播的钱
            row = assetUserAccountMapper.income(anchorUid, anchorIncome, 1);
            if(row < 1) {
                logger.error("consume: ERROR_CODE_706, 增加播主收入失败，请重试");
                return new JsonResult(ResultCode.ERROR_CODE_706,"增加播主收入失败，请重试");
            }

            // 添加收入记录
            AssetUserIncome assetUserIncome = new AssetUserIncome();
            assetUserIncome.setUid(anchorUid);
            if(anchorUserAccount.getOrganizationId() != null) {
                assetUserIncome.setOrgId(anchorUserAccount.getOrganizationId());
            }
            assetUserIncome.setConsumerUid(uid);
            assetUserIncome.setCoin(anchorIncome);
            assetUserIncome.setChatId(chatId);
            assetUserIncome.setType(Constants.AssetUserConsume.TYPE_CHAT);
            assetUserIncome.setCreateTime(new Date());
            row = assetUserIncomeMapper.insertSelective(assetUserIncome);
            if(row < 1) {
                logger.error("consume: ERROR_CODE_707, 生成播主收入记录，请重试");
                return new JsonResult(ResultCode.ERROR_CODE_707,"生成播主收入记录，请重试");
            }

            // 增加播主对应机构的钱
            if(organization != null && price > 0) {
                row = organizationMapper.income(organization.getId(), anchorIncome);
                if(row < 1) {
                    logger.error("consume: ERROR_CODE_708, 增加机构收入失败，请重试");
                    return new JsonResult(ResultCode.ERROR_CODE_708,"增加机构收入失败，请重试");
                }
            }

            // 添加平台收入记录
            AssetPlatformProfit assetPlatformProfit = new AssetPlatformProfit();
            assetPlatformProfit.setCoin((long) platformIncome);
            assetPlatformProfit.setIncomeId(assetUserIncome.getId());
            assetPlatformProfit.setRate(rate);
            assetPlatformProfit.setType(Constants.AssetUserConsume.TYPE_CHAT);
            assetPlatformProfit.setCreateTime(new Date());
            assetPlatformProfit.setAnchorUid(anchorUid);
            assetPlatformProfit.setConsumerUid(uid);
            assetPlatformProfit.setChatId(chatId);
            row = assetPlatformProfitMapper.insertSelective(assetPlatformProfit);
            if(row < 1) {
                logger.error("consume: ERROR_CODE_709, 生成平台收入记录，请重试");
                return new JsonResult(ResultCode.ERROR_CODE_709,"生成平台收入记录，请重试");
            }

            // 平台运营支出
            if(price > 0 && availableReceiveCoin > 0) {
                int opexCoin;
                if(availableReceiveCoin > price) {
                    opexCoin = price;
                } else {
                    opexCoin = availableReceiveCoin;
                }
                row = assetPlatformAccountMapper.opex(opexCoin);
                if(row < 1) {
                    logger.error("consume: ERROR_CODE_710, 更新平台支出异常，请重试");
                    return new JsonResult(ResultCode.ERROR_CODE_710,"更新平台支出异常，请重试");
                }
            }

            Chat updateChat = new Chat();
            updateChat.setId(chatId);
            updateChat.setUpdateTime(new Date());
            updateChat.setMinutes(chat.getMinutes() + 1);
            row = chatMapper.updateByPrimaryKeySelective(updateChat);
            if(row < 1) {
                logger.error("consume: ERROR_CODE_711, 更新聊天时间异常，请重试");
                return new JsonResult(ResultCode.ERROR_CODE_711,"更新聊天时间异常，请重试");
            }

            logger.info("consume: 消费成功");
            isSuccess = true;
            JsonResult response = JsonResult.success();
//            response.setBody(assetUserAccount.getAvailableRechargeCoin() + assetUserAccount.getAvailableIncomeCoin() - price);
            response.setBody(assetUserAccount.getAvailableRechargeCoin() + assetUserAccount.getAvailableReceiveCoin() - price);
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            logger.info("consume: transaction isSuccess = {}", isSuccess);
            logger.info("consume: end {}--------------------------------------", System.currentTimeMillis());
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }

    }

    public JsonResult connect(Long uid, Long anchorUid, boolean isNeedPush, Integer liveMode, String userAgent) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            if(uid != null && anchorUid != null && uid.equals(anchorUid)) {
                return JsonResult.error("不能跟自己视频哦");
            }
            logger.info("connect: begin {}--------------------------------------", System.currentTimeMillis());
            logger.info("connect: uid = {}, anchorUid = {}", uid, anchorUid);
            AssetUserAccount targetUser = assetUserAccountMapper.selectByUidForUpdate(anchorUid);
            if(targetUser == null) {
                logger.error("connect: anchorUid = {}，主播不存在", anchorUid);
                return JsonResult.error("主播不存在");
            }
            logger.info("connect: anchorStatus = {}", targetUser.getOnlineStatus());
            if(targetUser.getOnlineStatus() != Constants.User.ONLINE_STATUS_YES) {
                logger.error("connect: anchorUid = {}，对方休息了，给她私信约下次聊吧", anchorUid);
                if(targetUser.getSex() == Constants.User.SEX_WOMEN) {
                    return JsonResult.error("对方休息了，给她私信约下次聊吧");
                } else if(targetUser.getSex() == Constants.User.SEX_MEN) {
                    return JsonResult.error("对方休息了，给他私信约下次聊吧");
                } else  {
                    return JsonResult.error("对方休息了，给TA私信约下次聊吧");
                }
            }

            // 检查是否被播主拉黑
            Blacklist blacklist = blacklistService.selectByUidAndBlackUid(anchorUid, uid);
            if(blacklist != null) {
                return JsonResult.error("对方不能接听你的电话");
            }

            // 创建聊天关系
            Chat chat = new Chat();
            chat.setUid(uid);
            chat.setAnchorUid(anchorUid);
            chat.setBeginTime(new Date());
            chat.setUpdateTime(new Date());
            chat.setMinutes(0);
            chat.setStatus(Constants.Chat.STATUS_CONNECTING);
            chat.setType(Constants.Chat.TYPE_NORMAL);
            int row = chatMapper.insertSelective(chat);
            if(row > 0 && chat.getId() > 0) {
                // 更新自己为busy状态
                assetUserAccountMapper.updateOnlineStatusByUid(uid, Constants.User.ONLINE_STATUS_BUSY);
//                assetUserAccountMapper.updateOnlineStatusByUid(chat.getAnchorUid(), Constants.User.ONLINE_STATUS_BUSY);
                // 更新播主连接次数+1
                assetUserAccountMapper.updateConnectTimes(anchorUid);
                JsonResult jsonResult = JsonResult.success("拨打成功，正在等待对方接受邀请...");
                HashMap<String, Long> map = new HashMap<>();
                map.put("chatId", chat.getId());
                map.put("anchorId", anchorUid);
                jsonResult.setBody(map);
                isSuccess = true;

                // v2版本connect需要服务器发送push给对方
                if(isNeedPush) {
                    asyncSendConnectedPush(String.valueOf(chat.getId()), uid, String.valueOf(anchorUid), liveMode, userAgent);
                }

                logger.info("connect: chatId = {}，拨打成功，正在等待对方接受邀请...", chat.getId());
                return jsonResult;
            } else {
                logger.error("connect: error 创建聊天关系失败，请重试");
                return JsonResult.error("创建聊天关系失败，请重试");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            logger.info("connect: transaction isSuccess = {}", isSuccess);
            logger.info("connect: end {}--------------------------------------", System.currentTimeMillis());
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }
    }

    private void asyncSendConnectedPush(String chatId, Long fromUserId, String targetUserId, Integer liveMode, String userAgent) {
        ThreadPoolManager.getInstance().addExecuteTask(new Runnable() {
            @Override
            public void run() {
                try {
                    logger.info("connect: Push, chatId={}, fromUserId={}, targetUserId={}", chatId, fromUserId, targetUserId);
                    // 查询被呼叫用户deviceToken
                    User targetUser = userService.getUserById(Long.valueOf(targetUserId));
                    // deviceToken不为空，走VoIP推送
                    if(targetUser != null && StringUtils.isNotEmpty(targetUser.getDeviceToken())) {
                        // 判断当前deviceToken登录的用户是不是targetUserId用户
                        // TODO: 2017/11/27 已知问题：A、B先后登录一个设备，给A打电话，但是B收到的push，原因是部分iOS设备无法获取ID，默认是00000000-0000-0000-0000-000000000000，后续iOS端上优化再打开
//                        Device device = deviceMapper.selectByPrimaryKey(targetUser.getLastLoginDeviceId());
//                        if(device.getLoginUid() != null && device.getLoginUid().equals(targetUser.getId())) {
                            logger.info("connect: Push= online VoIP, targetUser.getDeviceToken()={}", targetUser.getDeviceToken());
                            AssetUserAccount fromUser = assetUserAccountMapper.selectByUid(fromUserId);
                            CallMessage callMessage = new CallMessage(chatId, String.valueOf(fromUser.getUid()),
                                    fromUser.getAvatar(), fromUser.getNickname(), liveMode);
                            String alertTitle = fromUser.getNickname() + " 邀请你进行视频通话";
                            PushService.sendPush(callMessage, alertTitle, targetUser.getDeviceToken());

                            // 审核中版本，同时发送sandbox push
                            boolean isInReviewVersion = appRecordService.checkIsInReview(userAgent);
                            if(isInReviewVersion) {
                                PushService.sendPush(callMessage, alertTitle, targetUser.getDeviceToken(), false);
                            }
//                        }
                    } else { // 走极光push
                        logger.info("connect: Push=jPush");
                        AssetUserAccount fromUser = assetUserAccountMapper.selectByUid(fromUserId);
                        CallMessage callMessage = new CallMessage(chatId, String.valueOf(fromUser.getUid()),
                                fromUser.getAvatar(), fromUser.getNickname(), liveMode);
                        JPushClient jPushClient = new JPushClient(pushConfig.getAppSecret(), pushConfig.getAppKey());
                        HashMap<String, String> extras = new HashMap();
                        extras.put("msg_type", CallMessage.TYPE);
                        extras.put("msg_content", callMessage.toString());

                        IosNotification iosNotification = IosNotification.newBuilder()
                                .setAlert(fromUser.getNickname() + " 邀请你进行视频通话")
                                .setSound("ring.mp3")
                                .setContentAvailable(true)
                                .setMutableContent(false)
                                .addExtras(extras)
                                .build();

                        Notification notification = Notification.newBuilder().addPlatformNotification(iosNotification).build();

                        PushPayload pushPayload = PushPayload.newBuilder()
                                .setPlatform(Platform.android_ios())
                                .setAudience(Audience.alias(targetUserId))
                                .setNotification(notification)
                                .setOptions(Options.newBuilder().setApnsProduction(true).build())
                                .build();
                        PushResult pushResult = jPushClient.sendPush(pushPayload);
                        logger.info("connect: PushPayload={}", JSON.toJSONString(pushPayload));
                        logger.info("connect: PushResult={}", pushResult.toString());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public JsonResult accept(Long chatId, Long anchorUid, Long uid) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            logger.info("accept: begin {}--------------------------------------", System.currentTimeMillis());
            logger.info("accept: chatId={}, anchorUid = {}, uid = {}", chatId, anchorUid, uid);
            Chat chat = chatMapper.selectByIdForUpdate(chatId);
            if(chat == null || chat.getStatus() != Constants.Chat.STATUS_CONNECTING) {
                logger.error("accept:  聊天已被终止，chatId={}", chatId);
                if(chat != null) {
                    logger.error("accept: 当前聊天状态，status={}", chat.getStatus());
                }
                return JsonResult.error("聊天已被终止");
            }
            if(!anchorUid.equals(chat.getAnchorUid()) || !uid.equals(chat.getUid())) {
                logger.error("accept: chat.getAnchorUid()={}, chat.getUid()={}, 非法接听", chat.getAnchorUid(), chat.getUid());
                return JsonResult.error("非法接听");
            }

            Chat updateChat = new Chat();
            updateChat.setId(chat.getId());
            updateChat.setStatus(Constants.Chat.STATUS_ACCEPT);
            updateChat.setUpdateTime(new Date());
            int row = chatMapper.updateByPrimaryKeySelective(updateChat);
            if(row > 0) {
                userService.updateOnlineStatusById(anchorUid, Constants.User.ONLINE_STATUS_BUSY);
                assetUserAccountMapper.updateAcceptTimes(anchorUid);
                isSuccess = true;
                logger.info("accept: 接听成功，开始聊天吧");
                return JsonResult.success("接听成功，开始聊天吧");
            } else {
                logger.error("更新chat状态失败，chatId={}", chat.getId());
                return JsonResult.error();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            logger.info("accept: transaction isSuccess = {}", isSuccess);
            logger.info("accept: end {}--------------------------------------", System.currentTimeMillis());
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }
    }

    public JsonResult acceptCheck(Long chatId, Long anchorUid, Long uid) {
        Chat chat = chatMapper.selectByIdAndStatusForUpdate(chatId, Constants.Chat.STATUS_CONNECTING);
        if(chat == null) {
            return JsonResult.error("聊天关系不存在，或者已被对方终止");
        }
        if(!anchorUid.equals(chat.getAnchorUid()) || !uid.equals(chat.getUid())) {
            return JsonResult.error("非法接听");
        }
        return JsonResult.success("可以接听");
    }

    public JsonResult abort(Long chatId, Long uid, String userAgent) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        boolean isSuccess = false;
        try {
            logger.info("abort: begin {}--------------------------------------", System.currentTimeMillis());
            logger.info("abort: chatId={}, uid = {}", chatId, uid);
            Chat chat = chatMapper.selectByIdForUpdate(chatId);
            if(chat == null || (chat.getStatus() != Constants.Chat.STATUS_CONNECTING && chat.getStatus() != Constants.Chat.STATUS_ACCEPT)) {
                logger.error("abort: chatId={}, 聊天已被终止", chatId);
                return JsonResult.error("聊天已被终止");
            }
            boolean isAnchor;
            if(uid.equals(chat.getUid())) {
                isAnchor = false;
            } else if(uid.equals(chat.getAnchorUid())) {
                isAnchor = true;
            } else {
                return JsonResult.error("只能终止自己的聊天关系");
            }
            Chat updateChat = new Chat();
            updateChat.setId(chatId);
            updateChat.setUpdateTime(new Date());
            updateChat.setEndTime(new Date());
            updateChat.setStatus(isAnchor ? Constants.Chat.STATUS_ANCHOR_ABORT : Constants.Chat.STATUS_USER_ABORT);
            int row = chatMapper.updateByPrimaryKeySelective(updateChat);
            // 无论row是否成功，都更新双方为在线状态，异常停止的chat交给task处理
            userService.updateOnlineStatusById(chat.getAnchorUid(), Constants.User.ONLINE_STATUS_YES);
            userService.updateOnlineStatusById(chat.getUid(), Constants.User.ONLINE_STATUS_YES);
            logger.info("abort: 已成功终止聊天，更新双方为在线状态");

            if(!isAnchor && chat.getStatus() == Constants.Chat.STATUS_CONNECTING) {
                asyncSendCancelConnectPush(String.valueOf(chatId), uid, String.valueOf(chat.getAnchorUid()), userAgent);
            }
            isSuccess = true;
            return row > 0 ? JsonResult.success("您已成功终止聊天") : JsonResult.error();
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.error();
        } finally {
            logger.info("abort: transaction isSuccess = {}", isSuccess);
            logger.info("abort: end {}--------------------------------------", System.currentTimeMillis());
            if(isSuccess) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
            }
        }
    }

    private void asyncSendCancelConnectPush(String chatId, Long fromUserId, String targetUserId, String userAgent) {
        ThreadPoolManager.getInstance().addExecuteTask(new Runnable() {
            @Override
            public void run() {
                try {
                    logger.info("abort: Push, chatId={}, fromUserId={}, targetUserId={}", chatId, fromUserId, targetUserId);
                    // 查询被呼叫用户deviceToken
                    User targetUser = userService.getUserById(Long.valueOf(targetUserId));
                    // deviceToken不为空，走VoIP推送
                    if(targetUser != null && StringUtils.isNotEmpty(targetUser.getDeviceToken())) {
                        // 判断当前deviceToken登录的用户是不是targetUserId用户
                        Device device = deviceMapper.selectByPrimaryKey(targetUser.getLastLoginDeviceId());
                        if(device.getLoginUid() != null && device.getLoginUid().equals(targetUser.getId())) {
                            logger.info("abort: Push=VoIP, targetUser.getDeviceToken()={}", targetUser.getDeviceToken());
                            AssetUserAccount fromUser = assetUserAccountMapper.selectByUid(fromUserId);
                            HandupMessage callMessage = new HandupMessage(chatId, String.valueOf(fromUser.getUid()),
                                    fromUser.getAvatar(), fromUser.getNickname());
                            String alertTitle = fromUser.getNickname() + " 已取消通话";
                            PushService.sendPush(callMessage, alertTitle, targetUser.getDeviceToken());

                            // 审核中版本，同时发送sandbox push
                            boolean isInReviewVersion = appRecordService.checkIsInReview(userAgent);
                            if(isInReviewVersion) {
                                PushService.sendPush(callMessage, alertTitle, targetUser.getDeviceToken(), false);
                            }
                        }
                    } else {
                        logger.info("abort: Push deviceToken is null ");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

}
